<?php
global $CFG;
require_once($CFG->dirroot . '/blocks/formal_langs/langs_src/tools/JLexPHP/jlex.php');
require_once($CFG->dirroot . '/blocks/formal_langs/tokens_base.php');
require_once($CFG->dirroot . '/blocks/formal_langs/langs_code/predefined/json_parser.php');
%%

%class json_lexer
%function next_token
%char
%line
%state STRING

HEX_D        = [a-fA-F0-9]
INT          = [-]?[0-9]+
DOUBLE       = {INT}((\.[0-9]+)?([eE][-+]?[0-9]+)?)
WS           = [ \t\r\n]
UNESCAPED_CH = [^\"\\]


%{
    private $token_count = 0;

    public $errors = array();
    
    // buffer for accumulating string
    private $string_buffer;
    // begin and end line for position
    private $string_pos_bl, $string_pos_el;
    // begin and end column for position
    private $string_pos_bc, $string_pos_ec;

    private function return_token($type, $value, $desc) {
        // create token object
        $res = new block_formal_langs_token_base(NULL, $type, $value, $this->return_pos(), $this->token_count);
        $res->set_description($desc);
        // increase token count
        $this->token_count++;

        return $res;
    }

    private function return_pos() {
        $begin_line = $this->yyline;
        $begin_col = $this->yycol;

        if(strpos($this->yytext(), '\n')) {
            $lines = explode("\n", $this->yytext());
            $num_lines = count($lines);
            
            $end_line = $begin_line + $num_lines - 1;
            $end_col = strlen($lines[$num_lines -1]);
        } else {
            $end_line = $begin_line;
            $end_col = $begin_col + strlen($this->yytext());
        }
        
        $res = new block_formal_langs_node_position($begin_line, $end_line, $begin_col, $end_col);
        
        return $res;
    }

    private function return_string() {
        // create position object
        $pos = new block_formal_langs_node_position($this->string_pos_bl, $this->string_pos_el, $this->string_pos_bc, $this->string_pos_ec);
        // create token object
        $res = new block_formal_langs_token_base(NULL, json_yyParser::STRING, $this->string_buffer, $pos, $this->token_count);
        $res->set_description("STRING");
        $this->token_count++;
        $this->string_buffer = '';

        return $res;
    }
%}

%%

<YYINITIAL> \" {
    $this->yybegin(self::STRING);
    $this->string_buffer = '';
    $this->string_pos_bl = $this->yyline;
    $this->string_pos_bc = $this->yycol;
 }

<STRING> {UNESCAPED_CH}+ { $this->string_buffer .= $this->yytext(); }

<STRING> \\\" { $this->string_buffer .= "\""; }
<STRING> \\\\ { $this->string_buffer .= "\\"; }
<STRING> \\\/ { $this->string_buffer .= "/"; }
<STRING> \\b  { $this->string_buffer .= "\b"; }
<STRING> \\f  { $this->string_buffer .= "\f"; }
<STRING> \\r  { $this->string_buffer .= "\r"; }
<STRING> \\n  { $this->string_buffer .= "\n"; }
<STRING> \\t  { $this->string_buffer .= "\t"; }
<STRING> \\   { $this->string_buffer .= "\\"; }

<STRING> \\u{HEX_D}{HEX_D}{HEX_D}{HEX_D} {
    $sub = substr($this->yytext(), 2);
    $num = intval($sub, 16);
    $this->string_buffer .= chr($num);
 }
        
<STRING> \" {
    $this->string_pos_el = $this->yyline;
    $this->string_pos_ec = $this->yycol;
    $this->yybegin(self::YYINITIAL);

    return $this->return_string();
 }

<YYINITIAL> {INT}    { return $this->return_token(json_yyParser::INT,     $this->yytext(), "INT"); }
<YYINITIAL> {DOUBLE} { return $this->return_token(json_yyParser::DOUBLE,  $this->yytext(), "DOUBLE"); }
<YYINITIAL> "true"   { return $this->return_token(json_yyParser::BOOLEAN, $this->yytext(), "BOOLEAN"); }
<YYINITIAL> "false"  { return $this->return_token(json_yyParser::BOOLEAN, $this->yytext(), "BOOLEAN"); }
<YYINITIAL> "null"   { return $this->return_token(json_yyParser::NULL,    $this->yytext(), "NULL"); }
<YYINITIAL> "{"      { return $this->return_token(json_yyParser::LBRACE,  $this->yytext(), "LBRACE"); }
<YYINITIAL> "}"      { return $this->return_token(json_yyParser::RBRACE,  $this->yytext(), "RBRACE"); }
<YYINITIAL> \[       { return $this->return_token(json_yyParser::LSBRACE, $this->yytext(), "LSBRACE"); }
<YYINITIAL> \]       { return $this->return_token(json_yyParser::RSBRACE, $this->yytext(), "RSBRACE"); }
<YYINITIAL> :        { return $this->return_token(json_yyParser::COLON,   $this->yytext(), "COLON"); }
<YYINITIAL> ,        { return $this->return_token(json_yyParser::COMMA,   $this->yytext(), "COMMA"); }

<YYINITIAL> {WS}+ {}

. { /* process bad input */
    $error = array();
    $error['line'] = $this->yyline;
    $error['column'] = $this->yycol;
    $error['input'] = $this->yytext();

    $this->errors[] = $error;
}
